package com.jse.util;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.jse.Strings;

/**
 * {@link PathMatcher}实现类，用于Ant风格的路径模式匹配。
 *
 * <p>此匹配器根据以下规则匹配URL：<br>
 * <ul>
 * <li>{@code?}匹配一个字符</li>
 * <li>{@code *}匹配零个或多个字符</li>
 * <li>{@code **}匹配路径中的零个或多个目录</li>
 * <li>{@code {jse:[a-z]+}}匹配正则表达式{@code [a-z]+}作为名为"jse"的路径变量</li>
 * </ul>
 *
 * <h3>示例</h3>
 * <ul>
 * <li>{@code com/t?st.jsp} - 匹配{@code com/test.jsp}，也匹配{@code com/tast.jsp}或{@code com/txst.jsp}</li>
 * <li>{@code com/*.jsp} - 匹配{@code com}目录下的所有{@code.jsp}文件</li>
 * <li><code>com/&#42;&#42;/test.jsp</code> - 匹配{@code com}路径下的所有{@code test.jsp}文件</li>
 * <li><code>com/jse/&#42;&#42;/*.jsp</code> - 匹配{@code com/jse}路径下的所有{@code.jsp}文件</li>
 * <li><code>org/&#42;&#42;/servlet/bla.jsp</code> - 匹配{@code com/jse/servlet/bla.jsp}，也匹配{@code com/jse/testing/servlet/bla.jsp}和{@code com/servlet/bla.jsp}</li>
 * <li>{@code com/{filename:\\w+}.jsp}将匹配{@code com/test.jsp}并将值{@code test}分配给变量{@code filename}</li>
 * </ul>
 *
 * <p><strong>注意：</strong>模式和路径必须都是绝对路径或都是相对路径才能匹配。因此，建议此实现的用户清理模式，以便在有意义的情况下在它们前面加上“/”。
 */
public class PathMatcher {

    /** 默认路径分隔符："/"。 */
    public static final String DEFAULT_PATH_SEPARATOR = "/";

    /** 缓存关闭阈值。 */
    private static final int CACHE_TURNOFF_THRESHOLD = 65536;

    /** 用于匹配路径变量的正则表达式。 */
    private static final Pattern VARIABLE_PATTERN = Pattern.compile("\\{[^/]+?\\}");

    /** 通配符字符数组。 */
    private static final char[] WILDCARD_CHARS = {'*', '?', '{'};
    /** 空字符串数组。 */
    private static final String[] EMPTY_STRING_ARRAY = {};

    /** 路径分隔符。 */
    private String pathSeparator;

    /** 路径分隔符模式缓存。 */
    private PathSeparatorPatternCache pathSeparatorPatternCache;

    /** 是否区分大小写。 */
    private boolean caseSensitive = true;

    /** 是否裁剪标记。 */
    private boolean trimTokens = false;

    /** 是否缓存模式的布尔值（可能为 null，表示自动决定）。 */
    private volatile Boolean cachePatterns;

    /** 已标记化的模式缓存（ConcurrentHashMap）。 */
    private final Map<String, String[]> tokenizedPatternCache = new ConcurrentHashMap<>(256);

    /** AntPathStringMatcher 的缓存（ConcurrentHashMap）。 */
    final Map<String, AntPathStringMatcher> stringMatcherCache = new ConcurrentHashMap<>(256);

    /**
     * 创建一个新的实例，使用默认路径分隔符。
     */
    public PathMatcher() {
        this.pathSeparator = DEFAULT_PATH_SEPARATOR;
        this.pathSeparatorPatternCache = new PathSeparatorPatternCache(DEFAULT_PATH_SEPARATOR);
    }

    /**
     * 使用自定义路径分隔符创建新实例的构造函数。
     * @param pathSeparator 要使用的路径分隔符，不能为空。
     * @since 4.1
     */
    public PathMatcher(String pathSeparator) {
//        Assert.notNull(pathSeparator, "'pathSeparator' must not be null");
        this.pathSeparator = (pathSeparator!= null? pathSeparator : DEFAULT_PATH_SEPARATOR);
        this.pathSeparatorPatternCache = new PathSeparatorPatternCache(pathSeparator);
    }

    /**
     * 设置用于模式解析的路径分隔符。
     * <p>默认为“/”，与 Ant 相同。
     */
    public void setPathSeparator(String pathSeparator) {
        this.pathSeparator = (pathSeparator!= null? pathSeparator : DEFAULT_PATH_SEPARATOR);
        this.pathSeparatorPatternCache = new PathSeparatorPatternCache(this.pathSeparator);
    }

    /**
     * 指定是否以区分大小写的方式执行模式匹配。
     * <p>默认为{@code true}。将此设置为{@code false}以进行不区分大小写的匹配。
     * @since 4.2
     */
    public void setCaseSensitive(boolean caseSensitive) {
        this.caseSensitive = caseSensitive;
    }

    /**
     * 指定是否裁剪标记化的路径和模式。
     * <p>默认为{@code false}。
     */
    public void setTrimTokens(boolean trimTokens) {
        this.trimTokens = trimTokens;
    }

    /**
     * 指定是否缓存传递到此匹配器的{@link #match}方法的模式的已解析模式元数据。{@code true}值激活无限模式缓存；{@code false}值完全关闭模式缓存。
     * <p>默认情况下，缓存处于打开状态，但具有在运行时遇到太多要缓存的模式时自动关闭缓存的变体（阈值为 65536），假设传入的模式是任意排列的，再次遇到重复模式的机会很小。
     * @since 4.0.1
     * @see #getStringMatcher(String)
     */
    public void setCachePatterns(boolean cachePatterns) {
        this.cachePatterns = cachePatterns;
    }

    /**
     * 停用模式缓存。
     */
    private void deactivatePatternCache() {
        this.cachePatterns = false;
        this.tokenizedPatternCache.clear();
        this.stringMatcherCache.clear();
    }

    /**
     * 判断给定的{@code path}是否表示一个可以被此接口的实现匹配的模式。
     * <p>如果返回值为{@code false}，则不必使用{@link #match}方法，因为对静态路径字符串的直接相等比较将得出相同的结果。
     * @param path 要检查的路径
     * @return 如果给定的{@code path}表示一个模式，则为{@code true}
     */
    public boolean isPattern(String path) {
        if (path == null) {
            return false;
        }
        boolean uriVar = false;
        for (int i = 0; i < path.length(); i++) {
            char c = path.charAt(i);
            if (c == '*' || c == '?') {
                return true;
            }
            if (c == '{') {
                uriVar = true;
                continue;
            }
            if (c == '}' && uriVar) {
                return true;
            }
        }
        return false;
    }

    /**
     * 根据此路径匹配器的匹配策略，将给定的{@code path}与给定的{@code pattern}进行匹配。
     * @param pattern 要匹配的模式
     * @param path 要测试的路径
     * @return 如果提供的{@code path}匹配，则为{@code true}，否则为{@code false}
     */
    public boolean match(String pattern, String path) {
        return doMatch(pattern, path, true, null);
    }

    /**
     * 根据此路径匹配器的匹配策略，将给定的{@code path}与给定的{@code pattern}的相应部分进行匹配。
     * <p>只是基本路径匹配,匹配比较多
     * @param pattern 要匹配的模式
     * @param path 要测试的路径
     * @return 如果提供的{@code path}匹配，则为{@code true}，否则为{@code false}
     */
    public boolean matchStart(String pattern, String path) {
        return doMatch(pattern, path, false, null);
    }

    /**
     * 实际将给定的{@code path}与给定的{@code pattern}进行匹配。
     * @param pattern 要匹配的模式
     * @param path 要测试的路径
     * @param fullMatch 是否需要完全模式匹配（否则，只做基本路径匹配，模式匹配就足够了）
     * @return 如果提供的{@code path}匹配，则为{@code true}，否则为{@code false}
     */
    protected boolean doMatch(String pattern, String path, boolean fullMatch,
                              Map<String, String> uriTemplateVariables) {

        // 如果路径为 null 或者路径和模式的起始分隔符不一致，则不匹配
        if (path == null || path.startsWith(this.pathSeparator)!= pattern.startsWith(this.pathSeparator)) {
            return false;
        }

        // 标记化模式
        String[] pattDirs = tokenizePattern(pattern);
        // 如果需要完全匹配且区分大小写且路径不是潜在匹配，则返回 false
        if (fullMatch && this.caseSensitive &&!isPotentialMatch(path, pattDirs)) {
            return false;
        }

        // 标记化路径
        String[] pathDirs = tokenizePath(path);
        int pattIdxStart = 0;
        int pattIdxEnd = pattDirs.length - 1;
        int pathIdxStart = 0;
        int pathIdxEnd = pathDirs.length - 1;

        // 匹配所有元素直到第一个"**"
        while (pattIdxStart <= pattIdxEnd && pathIdxStart <= pathIdxEnd) {
            String pattDir = pattDirs[pattIdxStart];
            if ("**".equals(pattDir)) {
                break;
            }
            if (!matchStrings(pattDir, pathDirs[pathIdxStart], uriTemplateVariables)) {
                return false;
            }
            pattIdxStart++;
            pathIdxStart++;
        }

        if (pathIdxStart > pathIdxEnd) {
            // 路径已耗尽，如果是完全匹配，则只有当模式剩余部分为"*"或"**"且路径以分隔符结尾时才匹配
            if (pattIdxStart > pattIdxEnd) {
                return (pattern.endsWith(this.pathSeparator) == path.endsWith(this.pathSeparator));
            }
            if (!fullMatch) {
                return true;
            }
            if (pattIdxStart == pattIdxEnd && pattDirs[pattIdxStart].equals("*") && path.endsWith(this.pathSeparator)) {
                return true;
            }
            for (int i = pattIdxStart; i <= pattIdxEnd; i++) {
                if (!pattDirs[i].equals("**")) {
                    return false;
                }
            }
            return true;
        } else if (pattIdxStart > pattIdxEnd) {
            // 字符串未耗尽，但模式已耗尽。匹配失败。
            return false;
        } else if (!fullMatch && "**".equals(pattDirs[pattIdxStart])) {
            // 由于模式中有"**"部分，路径起始部分肯定匹配。
            return true;
        }

        // 匹配到最后一个"**"
        while (pattIdxStart <= pattIdxEnd && pathIdxStart <= pathIdxEnd) {
            String pattDir = pattDirs[pattIdxEnd];
            if (pattDir.equals("**")) {
                break;
            }
            if (!matchStrings(pattDir, pathDirs[pathIdxEnd], uriTemplateVariables)) {
                return false;
            }
            if (pattIdxEnd == (pattDirs.length - 1)
                    && pattern.endsWith(this.pathSeparator)!= path.endsWith(this.pathSeparator)) {
                return false;
            }
            pattIdxEnd--;
            pathIdxEnd--;
        }

        if (pathIdxStart > pathIdxEnd) {
            // 字符串已耗尽
            for (int i = pattIdxStart; i <= pattIdxEnd; i++) {
                if (!pattDirs[i].equals("**")) {
                    return false;
                }
            }
            return true;
        }

        while (pattIdxStart!= pattIdxEnd && pathIdxStart <= pathIdxEnd) {
            int patIdxTmp = -1;
            for (int i = pattIdxStart + 1; i <= pattIdxEnd; i++) {
                if (pattDirs[i].equals("**")) {
                    patIdxTmp = i;
                    break;
                }
            }
            if (patIdxTmp == pattIdxStart + 1) {
                // '**/**'情况，跳过一个
                pattIdxStart++;
                continue;
            }
            // 在字符串的一部分中找到模式
            int patLength = (patIdxTmp - pattIdxStart - 1);
            int strLength = (pathIdxEnd - pathIdxStart + 1);
            int foundIdx = -1;

            strLoop:
            for (int i = 0; i <= strLength - patLength; i++) {
                for (int j = 0; j < patLength; j++) {
                    String subPat = pattDirs[pattIdxStart + j + 1];
                    String subStr = pathDirs[pathIdxStart + i + j];
                    if (!matchStrings(subPat, subStr, uriTemplateVariables)) {
                        continue strLoop;
                    }
                }
                foundIdx = pathIdxStart + i;
                break;
            }

            if (foundIdx == -1) {
                return false;
            }

            pattIdxStart = patIdxTmp;
            pathIdxStart = foundIdx + patLength;
        }

        for (int i = pattIdxStart; i <= pattIdxEnd; i++) {
            if (!pattDirs[i].equals("**")) {
                return false;
            }
        }

        return true;
    }

    /**
     * 判断路径是否是潜在匹配。
     * @param path 要检查的路径
     * @param pattDirs 模式的标记化部分
     * @return 如果路径是潜在匹配，则为{@code true}
     */
    private boolean isPotentialMatch(String path, String[] pattDirs) {
        if (!this.trimTokens) {
            int pos = 0;
            for (String pattDir : pattDirs) {
                int skipped = skipSeparator(path, pos, this.pathSeparator);
                pos += skipped;
                skipped = skipSegment(path, pos, pattDir);
                if (skipped < pattDir.length()) {
                    return (skipped > 0 || (pattDir.length() > 0 && isWildcardChar(pattDir.charAt(0))));
                }
                pos += skipped;
            }
        }
        return true;
    }

    /**
     * 跳过路径中的一部分。
     * @param path 要检查的路径
     * @param pos 当前位置
     * @param prefix 要跳过的部分
     * @return 跳过的长度
     */
    private int skipSegment(String path, int pos, String prefix) {
        int skipped = 0;
        for (int i = 0; i < prefix.length(); i++) {
            char c = prefix.charAt(i);
            if (isWildcardChar(c)) {
                return skipped;
            }
            int currPos = pos + skipped;
            if (currPos >= path.length()) {
                return 0;
            }
            if (c == path.charAt(currPos)) {
                skipped++;
            }
        }
        return skipped;
    }

    /**
     * 跳过路径中的分隔符。
     * @param path 要检查的路径
     * @param pos 当前位置
     * @param separator 分隔符
     * @return 跳过的长度
     */
	private int skipSeparator(String path, int pos, String separator) {
		int skipped = 0;
		while (path.startsWith(separator, pos + skipped)) {
			skipped += separator.length();
		}
		return skipped;
	}
	/**
     * 判断字符是否是通配符字符。
     * @param c 要检查的字符
     * @return 如果是通配符字符，则为{@code true}
     */
    private boolean isWildcardChar(char c) {
        for (char candidate : WILDCARD_CHARS) {
            if (c == candidate) {
                return true;
            }
        }
        return false;
    }

    /**
     * 根据给定的模式标记化路径模式。
     * <p>基于{@link #setCachePatterns}进行缓存，委托给{@link #tokenizePath(String)}进行实际的标记化算法。
     * @param pattern 要标记化的模式
     * @return 标记化后的模式部分
     */
    protected String[] tokenizePattern(String pattern) {
        String[] tokenized = null;
        Boolean cachePatterns = this.cachePatterns;
        if (cachePatterns == null || cachePatterns) {
            tokenized = this.tokenizedPatternCache.get(pattern);
        }
        if (tokenized == null) {
            tokenized = tokenizePath(pattern);
            if (cachePatterns == null && this.tokenizedPatternCache.size() >= CACHE_TURNOFF_THRESHOLD) {
                // 尝试适应运行时的情况：显然有太多不同的模式传入，因此关闭缓存，因为不太可能再次遇到重复的模式。
                deactivatePatternCache();
                return tokenized;
            }
            if (cachePatterns == null || cachePatterns) {
                this.tokenizedPatternCache.put(pattern, tokenized);
            }
        }
        return tokenized;
    }

    /**
     * 根据此匹配器的设置，将给定的路径标记化。
     * @param path 要标记化的路径
     * @return 标记化后的路径部分
     */
    protected String[] tokenizePath(String path) {
        return tokenizeToStringArray(path, this.pathSeparator, this.trimTokens, true);
    }

    /**
     * 测试一个字符串是否与一个模式匹配。
     * @param pattern 要匹配的模式（永远不为{@code null}）
     * @param str 必须与模式匹配的字符串（永远不为{@code null}）
     * @return 如果字符串与模式匹配，则为{@code true}，否则为{@code false}
     */
    private boolean matchStrings(String pattern, String str,
                                 Map<String, String> uriTemplateVariables) {

        return getStringMatcher(pattern).matchStrings(str, uriTemplateVariables);
    }

    /**
     * 构建或检索给定模式的{@link AntPathStringMatcher}。
     * <p>默认实现检查此 AntPathMatcher 的内部缓存（参见{@link #setCachePatterns}），如果没有找到缓存的副本，则创建一个新的 AntPathStringMatcher 实例。
     * <p>当在运行时遇到太多要缓存的模式时（阈值为 65536），它会关闭默认缓存，假设传入的模式是任意排列的，再次遇到重复模式的机会很小。
     * <p>可以重写此方法以实现自定义缓存策略。
     * @param pattern 要匹配的模式（永远不为{@code null}）
     * @return 相应的 AntPathStringMatcher（永远不为{@code null}）
     * @see #setCachePatterns
     */
    protected AntPathStringMatcher getStringMatcher(String pattern) {
        AntPathStringMatcher matcher = null;
        Boolean cachePatterns = this.cachePatterns;
        if (cachePatterns == null || cachePatterns) {
            matcher = this.stringMatcherCache.get(pattern);
        }
        if (matcher == null) {
            matcher = new AntPathStringMatcher(pattern, this.pathSeparator, this.caseSensitive);
            if (cachePatterns == null && this.stringMatcherCache.size() >= CACHE_TURNOFF_THRESHOLD) {
                // 尝试适应运行时的情况：显然有太多不同的模式传入，因此关闭缓存，因为不太可能再次遇到重复的模式。
                deactivatePatternCache();
                return matcher;
            }
            if (cachePatterns == null || cachePatterns) {
                this.stringMatcherCache.put(pattern, matcher);
            }
        }
        return matcher;
    }

    /**
     * 给定一个模式和一个完整路径，确定模式映射的部分。
     * <p>例如：<ul>
     * <li>'{@code /docs/cvs/commit.html}'和'{@code /docs/cvs/commit.html} → ''</li>
     * <li>'{@code /docs/*}'和'{@code /docs/cvs/commit} → '{@code cvs/commit}'</li>
     * <li>'{@code /docs/cvs/*.html}'和'{@code /docs/cvs/commit.html} → '{@code commit.html}'</li>
     * <li>'{@code /docs/**}'和'{@code /docs/cvs/commit} → '{@code cvs/commit}'</li>
     * <li>'{@code /docs/**\/*.html}'和'{@code /docs/cvs/commit.html} → '{@code cvs/commit.html}'</li>
     * <li>'{@code /*.html}'和'{@code /docs/cvs/commit.html} → '{@code docs/cvs/commit.html}'</li>
     * <li>'{@code *.html}'和'{@code /docs/cvs/commit.html} → '{@code /docs/cvs/commit.html}'</li>
     * <li>'{@code *}'和'{@code /docs/cvs/commit.html} → '{@code /docs/cvs/commit.html}'</li>
     * </ul>
     * <p>假设{@link #match}对于'{@code pattern}'和'{@code path}'返回{@code true}，但不强制执行此假设。
     * 给定一个模式和一个完整路径，确定模式映射的部分。
     * <p>此方法应该找出路径中哪些部分是通过实际模式动态匹配的，即从给定的完整路径中剥离静态定义的前导路径，仅返回路径中实际与模式匹配的部分。
     * <p>例如：对于“myroot/*.html”作为模式和“myroot/myfile.html”作为完整路径，此方法应返回“myfile.html”。详细的确定规则根据此路径匹配器的匹配策略指定。
     * <p>一个简单的实现可以在实际模式的情况下返回给定的完整路径，在模式不包含任何动态部分的情况下（即{@code pattern}参数是一个静态路径，不符合{@link #isPattern}的模式）返回空字符串。一个复杂的实现将区分给定路径模式的静态部分和动态部分。
     * @param pattern 路径模式
     * @param path 完整路径
     * @return 给定{@code path}的模式映射部分（永远不为{@code null}）
     */
    public String extractPathWithinPattern(String pattern, String path) {
        String[] patternParts = tokenizeToStringArray(pattern, this.pathSeparator, this.trimTokens, true);
        String[] pathParts = tokenizeToStringArray(path, this.pathSeparator, this.trimTokens, true);
        StringBuilder builder = new StringBuilder();
        boolean pathStarted = false;

        for (int segment = 0; segment < patternParts.length; segment++) {
            String patternPart = patternParts[segment];
            if (patternPart.indexOf('*') > -1 || patternPart.indexOf('?') > -1) {
                for (; segment < pathParts.length; segment++) {
                    if (pathStarted || (segment == 0 &&!pattern.startsWith(this.pathSeparator))) {
                        builder.append(this.pathSeparator);
                    }
                    builder.append(pathParts[segment]);
                    pathStarted = true;
                }
            }
        }

        return builder.toString();
    }

    /**
     * 给定一个模式和一个完整路径，提取 URI 模板变量。URI 模板变量通过花括号“{”和“}”表示。
     * <p>例如：对于模式“/hotels/{hotel}”和路径“/hotels/1”，此方法将返回一个包含“hotel”→“1”的映射。
     * @param pattern 可能包含 URI 模板的路径模式
     * @param path 要从中提取模板变量的完整路径
     * @return 包含变量名作为键，变量值作为值的映射
     */
    public Map<String, String> extractUriTemplateVariables(String pattern, String path) {
        Map<String, String> variables = new LinkedHashMap<>();
        boolean result = doMatch(pattern, path, true, variables);
        if (!result) {
            throw new IllegalStateException("Pattern \"" + pattern + "\" is not a match for \"" + path + "\"");
        }
        return variables;
    }

    /**
     * 将两个模式组合成一个新的模式并返回。
     * <p>组合两个模式的完整算法取决于底层实现。
     * @param pattern1 第一个模式
     * @param pattern2 第二个模式
     * @return 两个模式的组合
     * @throws IllegalArgumentException 当两个模式无法组合时
     *
     * 组合两个模式。
     * <p>此实现简单地将两个模式连接起来，除非第一个模式包含文件扩展名匹配（例如，{@code *.html}）。在这种情况下，第二个模式将合并到第一个模式中。否则，将抛出{@code IllegalArgumentException}。
     * <h4>示例</h4>
     * <table border="1">
     * <tr><th>模式 1</th><th>模式 2</th><th>结果</th></tr>
     * <tr><td>{@code null}</td><td>{@code null}</td><td>&nbsp;</td></tr>
     * <tr><td>/hotels</td><td>{@code null}</td><td>/hotels</td></tr>
     * <tr><td>{@code null}</td><td>/hotels</td><td>/hotels</td></tr>
     * <tr><td>/hotels</td><td>/bookings</td><td>/hotels/bookings</td></tr>
     * <tr><td>/hotels</td><td>bookings</td><td>/hotels/bookings</td></tr>
     * <tr><td>/hotels/*</td><td>/bookings</td><td>/hotels/bookings</td></tr>
     * <tr><td>/hotels/&#42;&#42;</td><td>/bookings</td><td>/hotels/&#42;&#42;/bookings</td></tr>
     * <tr><td>/hotels</td><td>{hotel}</td><td>/hotels/{hotel}</td></tr>
     * <tr><td>/hotels/*</td><td>{hotel}</td><td>/hotels/{hotel}</td></tr>
     * <tr><td>/hotels/&#42;&#42;</td><td>{hotel}</td><td>/hotels/&#42;&#42;/{hotel}</td></tr>
     * <tr><td>/*.html</td><td>/hotels.html</td><td>/hotels.html</td></tr>
     * <tr><td>/*.html</td><td>/hotels</td><td>/hotels.html</td></tr>
     * <tr><td>/*.html</td><td>/*.txt</td><td>{@code IllegalArgumentException}</td></tr>
     * </table>
     * @param pattern1 第一个模式
     * @param pattern2 第二个模式
     * @return 两个模式的组合
     * @throws IllegalArgumentException 如果两个模式无法组合
     */
    public String combine(String pattern1, String pattern2) {
        if (!Strings.hasText(pattern1) &&!Strings.hasText(pattern2)) {
            return "";
        }
        if (!Strings.hasText(pattern1)) {
            return pattern2;
        }
        if (!Strings.hasText(pattern2)) {
            return pattern1;
        }

        boolean pattern1ContainsUriVar = (pattern1.indexOf('{')!= -1);
        if (!pattern1.equals(pattern2) &&!pattern1ContainsUriVar && match(pattern1, pattern2)) {
            // /* + /hotel -> /hotel ; "/*.*" + "/*.html" -> /*.html
            // However /user + /user -> /usr/user ; /{foo} + /bar -> /{foo}/bar
            return pattern2;
        }

        // /hotels/* + /booking -> /hotels/booking
        // /hotels/* + booking -> /hotels/booking
        if (pattern1.endsWith(this.pathSeparatorPatternCache.getEndsOnWildCard())) {
            return concat(pattern1.substring(0, pattern1.length() - 2), pattern2);
        }

        // /hotels/** + /booking -> /hotels/**/booking
        // /hotels/** + booking -> /hotels/**/booking
        if (pattern1.endsWith(this.pathSeparatorPatternCache.getEndsOnDoubleWildCard())) {
            return concat(pattern1, pattern2);
        }

        int starDotPos1 = pattern1.indexOf("*.");
        if (pattern1ContainsUriVar || starDotPos1 == -1 || this.pathSeparator.equals(".")) {
            // 简单地连接两个模式
            return concat(pattern1, pattern2);
        }

        String ext1 = pattern1.substring(starDotPos1 + 1);
        int dotPos2 = pattern2.indexOf('.');
        String file2 = (dotPos2 == -1? pattern2 : pattern2.substring(0, dotPos2));
        String ext2 = (dotPos2 == -1? "" : pattern2.substring(dotPos2));
        boolean ext1All = (ext1.equals(".*") || ext1.isEmpty());
        boolean ext2All = (ext2.equals(".*") || ext2.isEmpty());
        if (!ext1All &&!ext2All) {
            throw new IllegalArgumentException("Cannot combine patterns: " + pattern1 + " vs " + pattern2);
        }
        String ext = (ext1All? ext2 : ext1);
        return file2 + ext;
    }

    /**
     * 连接两个路径。
     * @param path1 第一个路径
     * @param path2 第二个路径
     * @return 连接后的路径
     */
	private String concat(String path1, String path2) {
		boolean path1EndsWithSeparator = path1.endsWith(this.pathSeparator);
		boolean path2StartsWithSeparator = path2.startsWith(this.pathSeparator);

		if (path1EndsWithSeparator && path2StartsWithSeparator) {
			return path1 + path2.substring(1);
		}
		else if (path1EndsWithSeparator || path2StartsWithSeparator) {
			return path1 + path2;
		}
		else {
			return path1 + this.pathSeparator + path2;
		}
	}

	/**
     * 给定一个完整路径，返回一个适合对模式进行显式排序的{@link Comparator}。
     * <p>使用的完整算法取决于底层实现，但通常，返回的{@code Comparator}将对列表进行排序，使更具体的模式排在通用模式之前。
     * @param path 用于比较的完整路径
     * @return 能够按照显式程度对模式进行排序的比较器
     *
     * 给定一个完整路径，返回一个{@link Comparator}，适合用于按照显式程度对模式进行排序。
     * <p>此{@code Comparator}将对列表进行排序，使更具体的模式（没有 URI 模板或通配符的模式）排在通用模式之前。因此，给定一个包含以下模式的列表时，返回的比较器将对该列表进行排序，使其顺序如所示。
     * <ol>
     * <li>{@code /hotels/new}</li>
     * <li>{@code /hotels/{hotel}}</li>
     * <li>{@code /hotels/*}</li>
     * </ol>
     * <p>作为参数给出的完整路径用于测试精确匹配。因此，当给定的路径是{@code /hotels/2}时，模式{@code /hotels/2}将在{@code /hotels/1}之前排序。
     * @param path 用于比较的完整路径
     * @return 能够按照显式程度对模式进行排序的比较器
     */
    public Comparator<String> getPatternComparator(String path) {
        return new AntPatternComparator(path, this.pathSeparator);
    }

    /**
     * 通过{@link Pattern}测试一个字符串是否与一个模式匹配。
     * <p>模式可以包含特殊字符：'*'表示零个或多个字符；'?'表示一个且仅一个字符；'{'和'}'表示 URI 模板模式。例如{@code /users/{user}}。
     */
    protected static class AntPathStringMatcher {

    	        /**
    	         * 默认的变量模式字符串。
    	         */
    	        private static final String DEFAULT_VARIABLE_PATTERN = "((?s).*)";

    	        /**
    	         * 原始的模式字符串。
    	         */
    	        private final String rawPattern;

    	        /**
    	         * 是否区分大小写。
    	         */
    	        private final boolean caseSensitive;

    	        /**
    	         * 是否是精确匹配。
    	         */
    	        private final boolean exactMatch;

    	        /**
    	         * 编译后的正则表达式模式。
    	         */
    	        private final Pattern pattern;

    	        /**
    	         * 变量名列表。
    	         */
    	        private final List<String> variableNames = new ArrayList<>();

    	        /**
    	         * 构造函数，用于创建一个 AntPathStringMatcher 对象。
    	         * @param pattern 要匹配的模式字符串。
    	         * @param pathSeparator 路径分隔符。
    	         * @param caseSensitive 是否区分大小写。
    	         */
    	        protected AntPathStringMatcher(String pattern, String pathSeparator, boolean caseSensitive) {
    	            this.rawPattern = pattern;
    	            this.caseSensitive = caseSensitive;
    	            StringBuilder patternBuilder = new StringBuilder();
    	            Matcher matcher = getGlobPattern(pathSeparator).matcher(pattern);
    	            int end = 0;
    	            while (matcher.find()) {
    	                patternBuilder.append(quote(pattern, end, matcher.start()));
    	                String match = matcher.group();
    	                if ("?".equals(match)) {
    	                    patternBuilder.append('.');
    	                } else if ("*".equals(match)) {
    	                    patternBuilder.append(".*");
    	                } else if (match.startsWith("{") && match.endsWith("}")) {
    	                    int colonIdx = match.indexOf(':');
    	                    if (colonIdx == -1) {
    	                        patternBuilder.append(DEFAULT_VARIABLE_PATTERN);
    	                        this.variableNames.add(matcher.group(1));
    	                    } else {
    	                        String variablePattern = match.substring(colonIdx + 1, match.length() - 1);
    	                        patternBuilder.append('(');
    	                        patternBuilder.append(variablePattern);
    	                        patternBuilder.append(')');
    	                        String variableName = match.substring(1, colonIdx);
    	                        this.variableNames.add(variableName);
    	                    }
    	                }
    	                end = matcher.end();
    	            }
    	            // 如果没有找到通配符模式，这是一个精确的字符串匹配。
    	            if (end == 0) {
    	                this.exactMatch = true;
    	                this.pattern = null;
    	            } else {
    	                this.exactMatch = false;
    	                patternBuilder.append(quote(pattern, end, pattern.length()));
    	                this.pattern = Pattern.compile(patternBuilder.toString(),
    	                        Pattern.DOTALL | (this.caseSensitive? 0 : Pattern.CASE_INSENSITIVE));
    	            }
    	        }

    	        /**
    	         * 获取用于匹配通配符模式的正则表达式的模式。
    	         * @param pathSeparator 路径分隔符。
    	         * @return 用于匹配通配符模式的正则表达式模式。
    	         */
    	        private static Pattern getGlobPattern(String pathSeparator) {
    	            String pattern = "\\?|\\*|\\{((?:\\{[^" + pathSeparator + "]+?\\}|[^" + pathSeparator + "{}]|\\\\[{}])+?)\\}";
    	            return Pattern.compile(pattern);
    	        }

    	        /**
    	         * 引用字符串的一部分。
    	         * @param s 要引用的字符串。
    	         * @param start 开始位置。
    	         * @param end 结束位置。
    	         * @return 引用后的字符串部分。
    	         */
    	        private String quote(String s, int start, int end) {
    	            if (start == end) {
    	                return "";
    	            }
    	            return Pattern.quote(s.substring(start, end));
    	        }

    	        /**
    	         * 主要入口点，用于判断一个字符串是否与模式匹配。
    	         * @param str 要匹配的字符串。
    	         * @param uriTemplateVariables URI 模板变量的映射。
    	         * @return 如果字符串与模式匹配，则为{@code true}，否则为{@code false}。
    	         */
		public boolean matchStrings(String str,  Map<String, String> uriTemplateVariables) {
			if (this.exactMatch) {
				return this.caseSensitive ? this.rawPattern.equals(str) : this.rawPattern.equalsIgnoreCase(str);
			}
			else if (this.pattern != null) {
				Matcher matcher = this.pattern.matcher(str);
				if (matcher.matches()) {
					if (uriTemplateVariables != null) {
						if (this.variableNames.size() != matcher.groupCount()) {
							throw new IllegalArgumentException("The number of capturing groups in the pattern segment " +
									this.pattern + " does not match the number of URI template variables it defines, " +
									"which can occur if capturing groups are used in a URI template regex. " +
									"Use non-capturing groups instead.");
						}
						for (int i = 1; i <= matcher.groupCount(); i++) {
							String name = this.variableNames.get(i - 1);
							if (name.startsWith("*")) {
								throw new IllegalArgumentException("Capturing patterns (" + name + ") are not " +
										"supported by the AntPathMatcher. Use the PathPatternParser instead.");
							}
							String value = matcher.group(i);
							uriTemplateVariables.put(name, value);
						}
					}
					return true;
				}
			}
			return false;
		}

	}


    /**
     * 默认的模式比较器实现，由{@link PathMatcher#getPatternComparator(String)}返回。
     * <p>按照以下顺序确定最“通用”的模式：
     * <ul>
     * <li>如果它为 null 或捕获所有模式（即等于“/**”）</li>
     * <li>如果另一个模式是实际匹配</li>
     * <li>如果它是捕获所有模式（即它以“**”结尾）</li>
     * <li>如果它比另一个模式有更多的“*”</li>
     * <li>如果它比另一个模式有更多的“{foo}”</li>
     * <li>如果它比另一个模式短</li>
     * </ul>
     */
    protected static class AntPatternComparator implements Comparator<String> {

        /**
         * 要比较的路径。
         */
        private final String path;

        /**
         * 路径分隔符。
         */
        private final String pathSeparator;

        /**
         * 构造函数，接受一个路径，并使用默认路径分隔符。
         * @param path 要比较的路径。
         */
        public AntPatternComparator(String path) {
            this(path, PathMatcher.DEFAULT_PATH_SEPARATOR);
        }

        /**
         * 构造函数，接受一个路径和一个路径分隔符。
         * @param path 要比较的路径。
         * @param pathSeparator 路径分隔符。
         */
        public AntPatternComparator(String path, String pathSeparator) {
            this.path = path;
            this.pathSeparator = pathSeparator;
        }

        /**
         * 比较两个模式以确定哪个应该首先匹配，即哪个对于当前路径最具体。
         * @return 一个负整数、零或正整数，表示模式 1 比模式 2 更具体、同样具体或不太具体。
         */
        @Override
        public int compare(String pattern1, String pattern2) {
            PatternInfo info1 = new PatternInfo(pattern1, this.pathSeparator);
            PatternInfo info2 = new PatternInfo(pattern2, this.pathSeparator);

            // 如果两个模式都是最不具体的，返回 0。
            if (info1.isLeastSpecific() && info2.isLeastSpecific()) {
                return 0;
            }
            // 如果模式 1 是最不具体的，返回 1。
            else if (info1.isLeastSpecific()) {
                return 1;
            }
            // 如果模式 2 是最不具体的，返回 -1。
            else if (info2.isLeastSpecific()) {
                return -1;
            }

            boolean pattern1EqualsPath = pattern1.equals(this.path);
            boolean pattern2EqualsPath = pattern2.equals(this.path);
            // 如果两个模式都等于给定路径，返回 0。
            if (pattern1EqualsPath && pattern2EqualsPath) {
                return 0;
            }
            // 如果模式 1 等于给定路径，返回 -1。
            else if (pattern1EqualsPath) {
                return -1;
            }
            // 如果模式 2 等于给定路径，返回 1。
            else if (pattern2EqualsPath) {
                return 1;
            }

            // 如果一个是前缀模式且另一个不是，根据长度比较。
            if (info1.isPrefixPattern() && info2.isPrefixPattern()) {
                return info2.getLength() - info1.getLength();
            } else if (info1.isPrefixPattern() && info2.getDoubleWildcards() == 0) {
                return 1;
            } else if (info2.isPrefixPattern() && info1.getDoubleWildcards() == 0) {
                return -1;
            }

            // 如果总计数不同，根据总计数比较。
            if (info1.getTotalCount()!= info2.getTotalCount()) {
                return info1.getTotalCount() - info2.getTotalCount();
            }

            // 如果长度不同，根据长度比较。
            if (info1.getLength()!= info2.getLength()) {
                return info2.getLength() - info1.getLength();
            }

            // 如果单星号数量不同，根据单星号数量比较。
            if (info1.getSingleWildcards() < info2.getSingleWildcards()) {
                return -1;
            } else if (info2.getSingleWildcards() < info1.getSingleWildcards()) {
                return 1;
            }

            // 如果变量数量不同，根据变量数量比较。
            if (info1.getUriVars() < info2.getUriVars()) {
                return -1;
            } else if (info2.getUriVars() < info1.getUriVars()) {
                return 1;
            }

            return 0;
        }

        /**
         * 值类，用于保存有关模式的信息，例如“*”、“**”和“{”模式元素的出现次数。
         */
        private static class PatternInfo {

            /**
             * 模式字符串。
             */
            private final String pattern;

            /**
             * 变量数量。
             */
            private int uriVars;

            /**
             * 单个星号数量。
             */
            private int singleWildcards;

            /**
             * 双星号数量。
             */
            private int doubleWildcards;

            /**
             * 是否是捕获所有模式。
             */
            private boolean catchAllPattern;

            /**
             * 是否是前缀模式。
             */
            private boolean prefixPattern;

            /**
             * 长度（考虑变量为长度 1）。
             */
            private Integer length;

            /**
             * 构造函数，接受一个模式字符串和一个路径分隔符。
             * @param pattern 模式字符串。
             * @param pathSeparator 路径分隔符。
             */
            PatternInfo(String pattern, String pathSeparator) {
                this.pattern = pattern;
                if (this.pattern!= null) {
                    initCounters();
                    this.catchAllPattern = this.pattern.equals(pathSeparator + "**");
                    this.prefixPattern =!this.catchAllPattern && this.pattern.endsWith(pathSeparator + "**");
                }
                if (this.uriVars == 0) {
                    this.length = (this.pattern!= null? this.pattern.length() : 0);
                }
            }

            /**
             * 初始化计数器。
             */
            protected void initCounters() {
                int pos = 0;
                if (this.pattern!= null) {
                    while (pos < this.pattern.length()) {
                        if (this.pattern.charAt(pos) == '{') {
                            this.uriVars++;
                            pos++;
                        } else if (this.pattern.charAt(pos) == '*') {
                            if (pos + 1 < this.pattern.length() && this.pattern.charAt(pos + 1) == '*') {
                                this.doubleWildcards++;
                                pos += 2;
                            } else if (pos > 0 &&!this.pattern.substring(pos - 1).equals(".*")) {
                                this.singleWildcards++;
                                pos++;
                            } else {
                                pos++;
                            }
                        } else {
                            pos++;
                        }
                    }
                }
            }

            /**
             * 获取变量数量。
             * @return 变量数量。
             */
            public int getUriVars() {
                return this.uriVars;
            }

            /**
             * 获取单个星号数量。
             * @return 单个星号数量。
             */
            public int getSingleWildcards() {
                return this.singleWildcards;
            }

            /**
             * 获取双星号数量。
             * @return 双星号数量。
             */
            public int getDoubleWildcards() {
                return this.doubleWildcards;
            }

            /**
             * 判断是否是最不具体的模式。
             * @return 如果是最不具体的模式，则为 true。
             */
            public boolean isLeastSpecific() {
                return (this.pattern == null || this.catchAllPattern);
            }

            /**
             * 判断是否是前缀模式。
             * @return 如果是前缀模式，则为 true。
             */
            public boolean isPrefixPattern() {
                return this.prefixPattern;
            }

            /**
             * 获取总计数。
             * @return 总计数。
             */
            public int getTotalCount() {
                return this.uriVars + this.singleWildcards + (2 * this.doubleWildcards);
            }

            /**
             * 返回给定模式的长度，其中模板变量被视为长度为 1。
             * @return 长度。
             */
            public int getLength() {
                if (this.length == null) {
                    this.length = (this.pattern!= null?
                            PathMatcher.VARIABLE_PATTERN.matcher(this.pattern).replaceAll("#").length() : 0);
                }
                return this.length;
            }
        }
    }


    /**
     * 一个简单的依赖于配置的路径分隔符的模式缓存。
     */
    private static class PathSeparatorPatternCache {

        /**
         * 以单个通配符结尾的字符串。
         */
        private final String endsOnWildCard;

        /**
         * 以双通配符结尾的字符串。
         */
        private final String endsOnDoubleWildCard;

        /**
         * 构造函数，接受一个路径分隔符并初始化以单个通配符和双通配符结尾的字符串。
         * @param pathSeparator 路径分隔符。
         */
        public PathSeparatorPatternCache(String pathSeparator) {
            this.endsOnWildCard = pathSeparator + "*";
            this.endsOnDoubleWildCard = pathSeparator + "**";
        }

        /**
         * 获取以单个通配符结尾的字符串。
         * @return 以单个通配符结尾的字符串。
         */
        public String getEndsOnWildCard() {
            return this.endsOnWildCard;
        }

        /**
         * 获取以双通配符结尾的字符串。
         * @return 以双通配符结尾的字符串。
         */
        public String getEndsOnDoubleWildCard() {
            return this.endsOnDoubleWildCard;
        }
    }

    /**
     * 使用 {@link StringTokenizer} 将给定的字符串拆分为字符串数组。
     * <p>给定的分隔符字符串可以由任意数量的分隔符字符组成。这些字符中的每一个都可以用于分隔标记。分隔符始终是单个字符；对于多字符分隔符，请考虑使用 {@link #delimitedListToStringArray}。
     * @param str 要进行标记化的字符串（可能为 null 或为空）。
     * @param delimiters 分隔符字符，组合成一个字符串（每个字符都单独被视为一个分隔符）。
     * @param trimTokens 通过 {@link String#trim()} 方法裁剪标记。
     * @param ignoreEmptyTokens 从结果数组中省略空标记（仅适用于裁剪后为空的标记；{@link StringTokenizer} 一开始就不会将后续分隔符视为标记）。
     * @return 标记组成的字符串数组。
     * @see java.util.StringTokenizer
     * @see String#trim()
     */
    protected String[] tokenizeToStringArray(
            String str, String delimiters, boolean trimTokens, boolean ignoreEmptyTokens) {
        if (str == null) {
            return EMPTY_STRING_ARRAY;
        }
        StringTokenizer st = new StringTokenizer(str, delimiters);
        List<String> tokens = new ArrayList<>();
        while (st.hasMoreTokens()) {
            String token = st.nextToken();
            if (trimTokens) {
                token = token.trim();
            }
            if (!ignoreEmptyTokens || token.length() > 0) {
                tokens.add(token);
            }
        }
        return tokens.isEmpty()? EMPTY_STRING_ARRAY : tokens.toArray(EMPTY_STRING_ARRAY);
    }

}